﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Dare.Utilities.IO
{
    public class FilePath : ICloneable
    {
        string originalString;
        public string OriginalString
        {
            get { return originalString; }
        }

        string rootPath;
        public string RootPath
        {
            get { return rootPath; }
        }

        string fileName;
        public string FileName
        {
            get { return fileName; }
        }

        public bool IsFile
        {
            get { return String.IsNullOrEmpty(fileName); }
        }

        List<string> subPaths;
        public IList<string> SubPaths
        {
            get { return subPaths.AsReadOnly(); }
        }

        string relativePath;
        public string RelativePath
        {
            get { return relativePath; }
        }

        string relativeFilePath;
        public string RelativeFilePath
        {
            get { return relativeFilePath; }
        }

        string fullPath;
        public string FullePath
        {
            get { return fullPath; }
        }

        string fullFilePath;
        public string FullFilePath
        {
            get { return fullFilePath; }
        }

        FilePathType filePathType;
        public FilePathType FilePathType
        {
            get { return filePathType; }
        }

        public FilePath()
        {
            Clear();
        }

        public FilePath(string path, bool identifyAsPath)
        {
            SetFullPath(path, identifyAsPath);
        }

        private void Clear()
        {
            rootPath = String.Empty;
            subPaths = new List<string>();
            fileName = String.Empty;
            relativePath = String.Empty;
            relativeFilePath = String.Empty;
            fullPath = String.Empty;
            fullFilePath = String.Empty;
            originalString = String.Empty;
            filePathType = FilePathType.Unkown;
        }

        public void SetFullPath(string path, bool identifyAsPath)
        {
            Clear();

            originalString = path;

            path = path.Trim();

            //判断文件路径类型
            if (path.Length > 2)
            {
                if (Char.IsLetter(path[0]) && path[1] == ':')
                {
                    rootPath = path[0] + @":";
                    filePathType = FilePathType.LocalPath;
                }
                else if (path.StartsWith(@"\\"))
                {
                    rootPath = @"\\";
                    filePathType = FilePathType.UncPath;
                }
                else if (path.StartsWith("/"))
                {
                    rootPath = "";
                    filePathType = FilePathType.LocalPath;
                }
                else if (path.StartsWith("http:", StringComparison.OrdinalIgnoreCase))
                {
                    rootPath = @"http://";
                    filePathType = FilePathType.HttpPath;
                }
                else if (path.StartsWith("ftp:", StringComparison.OrdinalIgnoreCase))
                {
                    rootPath = @"ftp://";
                    filePathType = FilePathType.FtpPath;
                }
            }

            //初始化根路径
            if (filePathType != FilePathType.Unkown)
            {
                string[] parts = path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
                int subPathStartIndex = 1;

                switch (filePathType)
                {
                    case FilePathType.LocalPath:
                        if (rootPath == "")    //UNIX
                        {
                            subPathStartIndex = 0;
                        }
                        else
                        {
                            subPathStartIndex = 1;
                        }
                        break;
                    case FilePathType.UncPath:
                        subPathStartIndex = 1;
                        if (parts.Length > 0)
                        {
                            rootPath += parts[0];
                        }
                        else
                        {
                            filePathType = FilePathType.Unkown;
                        }
                        break;
                    case FilePathType.FtpPath:
                    case FilePathType.HttpPath:
                        subPathStartIndex = 2;
                        if (parts.Length > 1)
                        {
                            rootPath += parts[1];
                        }
                        else
                        {
                            filePathType = FilePathType.Unkown;
                        }
                        break;
                }

                //初始化子路径
                for (int i = subPathStartIndex; i < parts.Length; i++)
                {
                    subPaths.Add(parts[i]);
                }

                //初始化文件文件名
                if (subPaths.Count > 0 && !path.EndsWith("/") && !path.EndsWith("\\") && !identifyAsPath)
                {
                    fileName = subPaths[subPaths.Count - 1];
                    subPaths.RemoveAt(subPaths.Count - 1);
                }

                Update();
            }
        }

        public FilePath ClearSubPath()
        {
            subPaths.Clear();
            Update();

            return this;
        }

        public FilePath AppendSubPath(string path)
        {
            if (path != null)
            {
                string[] parts = path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length > 0)
                {
                    subPaths.AddRange(parts);
                    Update();
                }                
            }

            return this;
        }

        public FilePath AppendFileName(string fileName)
        {
            if (fileName != null)
            {
                string[] parts = fileName.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length > 0)
                {
                    for (int i = 0; i < parts.Length - 1; i++)
                    {
                        subPaths.Add(parts[i]);
                    }
                    this.fileName = parts[parts.Length - 1];
                    Update();
                }
            }

            return this;
        }

        public FilePath SetFileName(string fileName)
        {
            this.fileName = fileName == null ? String.Empty : Path.GetFileName(fileName);
            Update();

            return this;
        }

        public string FormatSubPath(string path)
        {
            string[] parts = path.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length > 0)
            {
                return FormatSubPath(parts);
            }
            else
            {
                return String.Empty;
            }
        }

        public string FormatSubPath(params string[] subPaths)
        {
            List<string> parts = new List<string>();
            for (int i = 0; i < subPaths.Length; i++)
            {
                if (subPaths[i] != null)
                {
                    parts.AddRange(subPaths[i].Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries));
                }
            }

            return InnerFormatSubPath(parts.ToArray());
        }

        private string InnerFormatSubPath(params string[] subPaths)
        {
            switch (filePathType)
            {
                case FilePathType.LocalPath:
                case FilePathType.UncPath:
                    return String.Join(Path.DirectorySeparatorChar.ToString(), subPaths);
                case FilePathType.HttpPath:
                case FilePathType.FtpPath:
                    return String.Join("/", subPaths);
            }
            return String.Empty;
        }

        public void Update()
        {
            if (subPaths.Count > 0)
            {
                subPaths.Add("");
                relativePath = InnerFormatSubPath(subPaths.ToArray());
                subPaths.RemoveAt(subPaths.Count - 1);
                fullPath = InnerFormatSubPath(rootPath, relativePath);
            }
            else
            {
                relativePath = String.Empty;
                fullPath = InnerFormatSubPath(rootPath, relativePath);
            }
            relativeFilePath = relativePath + fileName;
            fullFilePath = fullPath + fileName;
        }

        public FilePath GetNewFilePath(string fileName)
        {
            FilePath filePath = CopyTo(new FilePath());
            filePath.SetFileName(fileName);
            return filePath;
        }

        public override string ToString()
        {
            return FullFilePath;
        }

        #region ICloneable 成员
        public FilePath CopyTo(FilePath filePath)
        {
            if (filePath == null) return null;

            filePath.originalString = this.originalString;
            filePath.rootPath = this.rootPath;
            filePath.subPaths.Clear();
            filePath.subPaths.AddRange(this.subPaths);
            filePath.relativePath = this.relativePath;
            filePath.fileName = this.fileName;
            filePath.fullPath = this.fullPath;
            filePath.filePathType = this.filePathType;

            return filePath;
        }

        public object Clone()
        {
            return CopyTo(new FilePath());
        }

        public FilePath CloneFilePath()
        {
            return CopyTo(new FilePath());
        }

        #endregion
    }

    public enum FilePathType
    {
        Unkown,
        LocalPath,
        UncPath,
        HttpPath,
        FtpPath,
    }
}
